/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "TextureAtlas.h"
#include "TextureSimple.h"
#include <lib3d/hard/Hardware.h>
#include <filesystem/String.hpp>
#include <xml/Stream.h>
#include <sstream>

namespace liba {
namespace lib3d {
namespace material {

class FrameDefinition:
	public xml::ObjectANSI,
	public AtlasFrame
{
	virtual bool on_attribute(xml::Provider * prov, const std::string & name, const std::string & value)
	{
		if(name == "Name")
			return converter::convert(value, &this->name);
		if(name == "X")
			return converter::convert(value, &this->rect.x);
		if(name == "Y")
			return converter::convert(value, &this->rect.y);
		if(name == "Width")
			return converter::convert(value, &this->rect.width);
		if(name == "Height")
			return converter::convert(value, &this->rect.height);
		if(name == "Rect")
			return converter::convert(value, &this->rect);

		return false;
	}
};


class AtlasDefinition: private xml::ObjectANSI
{
	virtual bool on_node(xml::Provider * prov, const std::string & name)
	{
		if(name == "Frame")
		{
			FrameDefinition frame;
			if (!prov->parse_object(&frame))
				return false;

			if (!frame.rect.width || !frame.rect.height)
				THROW(Gluck(__FUNCTION__, "Atlas width and height should be greater then 0"));

			textureFrames.push_back(frame);

			return true;
		}

		return false;
	}

	virtual bool on_attribute(xml::Provider * prov, const std::string & name, const std::string & value)
	{
		if (name == "TextureSrc")
			return converter::convert(value, &this->texturePath);

		return false;
	}

public:
	xml::ObjectANSI* GetLoader() { return this; }

public:
	filesystem::String texturePath;
	std::vector <AtlasFrame> textureFrames;
};




void TextureAtlasRep::load(const filesystem::String & name)
{
	std::auto_ptr<resource::Resource> gameResource(resource::Manager::create_resource(name));
	std::auto_ptr<AtlasDefinition> atlasDefinition(new AtlasDefinition());
	if (!xml::StreamResource(gameResource.get()).load(&xml::Root(atlasDefinition->GetLoader(), "Atlas")))
		THROW(GluckBadResource(__FUNCTION__, "Failed to load atlas definition resource", name));

	TextureSimpleName textureName(atlasDefinition->texturePath, false);
	this->texture.reset(new TextureSimple(Atom(), textureName));

	frames = atlasDefinition->textureFrames;

	stop_loading();
}

int TextureAtlasRep::GetWidth(bool forceLoad)
{
	if(!frames.size())
	{
		start_loading();
		return 0;
	}

	if(!texture.get() || !texture->get_rep())
		return 0;

	return texture->get_rep()->get_width(forceLoad);
}

int TextureAtlasRep::GetHeight(bool forceLoad)
{
	if(!frames.size())
	{
		start_loading();
		return 0;
	}

	if(!texture.get() || !texture->get_rep())
		return 0;

	return texture->get_rep()->get_height(forceLoad);
}

const AtlasFrame& TextureAtlasRep::GetFrame(size_t index) const
{
	if(index >= frames.size())
	{
		std::stringstream ss;
		ss << "Frame index " << (unsigned int)index << " is out of range";
		THROW(Gluck(__FUNCTION__, ss.str()));
	}

	return frames[index];
}

const AtlasFrame& TextureAtlasRep::GetFrame(const Atom &name) const
{
	for(std::vector <AtlasFrame>::const_iterator it = frames.begin(); it != frames.end(); it++)
		if((*it).name == name)
			return *it;

	std::stringstream ss;
	ss << "Can't found frame \"" << name.get_str().c_str() << "\"";
	THROW(Gluck(__FUNCTION__, ss.str()));
}

void TextureAtlasRep::render_to_list( hard::Hardware * hardware, hard::RenderTexture * render_texture )
{
	if(texture.get() && texture->get_rep())
		texture->get_rep()->render_to_list(hardware, render_texture);
}

TextureAtlas * TextureAtlas::clone()const
{
	return new TextureAtlas(*this);
}

} // namespace material
} // namespace lib3d
} // namespace liba

